%%%-------------------------------------------------------------------
%%% File    : ewgi_unit.erl
%%% Author  : filippo pacini <pacini@sgconsulting.it>
%%% License :
%%% The contents of this file are subject to the Mozilla Public
%%% License Version 1.1 (the "License"); you may not use this file
%%% except in compliance with the License. You may obtain a copy of
%%% the License at http://www.mozilla.org/MPL/
%%%
%%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and
%%% limitations under the License.
%%% The Initial Developer of the Original Code is S.G. Consulting
%%% srl. Portions created by S.G. Consulting s.r.l. are Copyright (C)
%%% 2006 S.G. Consulting srl. All Rights Reserved.
%%%
%%% Description : 
%%%
%%% Created :  4 Sep 2006 by filippo pacini <pacini@sgconsulting.it>
%%%-------------------------------------------------------------------
-module(ewgi_unit).

-export([run/1, 
         run_verbose/1,
         assert_equal/1, 
         assert_equal/2, 
         assert_not_equal/2,
         assert_bool/1,
         assert_match/2,
         assert_raises/2, 
         assert_raises/3, 
         fail/0]).

%% sgeunit test
-export([test_ok/0, test_fail/0, test_not_equal/0, test_equal_list/0, test_multiple/0, test_raises/0]).


%%--------------------
%%
%% Module API
%%
%%--------------------
run_verbose(ModuleList) ->
    ets:new(?MODULE, [public, named_table]),
    true = ets:insert(?MODULE, {verbose, true}),
    ?MODULE:run(ModuleList).

run([]) ->
    done;
run([M|T]) ->
    io:format("~n~nRunning ~p~n", [M]),
    ?MODULE:run(M),
    run(T);

%% Get list of exported functions from Module.
%% Execute setup_test if exported from Module
%% run all test_* in Module collecting results.
%% Execute end_test if exported from Module
run(Module) when is_atom(Module) ->
    ExportList = Module:module_info(exports),
    setup(Module, ExportList),
    run_tests(Module, ExportList, []),
    end_t(Module, ExportList).



%% Assert Val1 == Val2
assert_equal(Val1, Val2) ->
    case Val1 == Val2 of
        true ->
            report(ok, "[pass]  .... ~p=:=~p~n", [Val1, Val2]),
            ok;
        _ ->
            report(error, "[ERROR]  .... ~p=/=~p~n", [Val1, Val2]),
            {error, {assert_equal, Val1, Val2}}
    end.

assert_equal(ValueList) ->
    Test = lists:nth(1, ValueList),
    case lists:all(fun (El) -> El == Test end, ValueList) of
        true ->
            report(ok, "[pass]  .... ~p~n", [ValueList]),
            ok;
        _ ->
            report(error, "[ERROR] .... ~p=/=~p~n", [Test, ValueList]),
            {error, {assert_equal, ValueList}}
    end.

%% Assert Val1 != Val2
assert_not_equal(Val1, Val2) ->
    case Val1 == Val2 of
        true ->
            report(error, "[ERROR] .... ~p=:=~p~n", [Val1, Val2]),
            {error, {assert_not_equal, Val1, Val2}};
        _ ->
            report(ok, "[pass]  .... ~p=/=~p~n", [Val1, Val2]),
	    ok
    end.

assert_bool(true) ->
    report(ok, "[pass]  .... boolean is true~n", []),
    ok;
assert_bool(_) ->
    report(error, "[ERROR] .... boolean is false~n", []),
    {error, {assert_bool, false}}.

assert_match(X, Y) ->
    try X = Y of
        _ -> 
            report(ok, "[pass]  .... ~p=~p~n", [X, Y]),
            ok
    catch 
        error:Reason -> 
            report(error, "[ERROR] .... ~p does not match ~p~n", [X, Y]),
            {error, {assert_match, Reason}}
    end.

assert_raises(Fun, Error) ->
    Result = (catch Fun()),
    case Result of
        Error ->
            report(ok, "[pass]  .... ~p raises ~p~n", [Fun, Error]),
            ok;
        _ ->
            report(error, "[ERROR] .... ~p raises ~p~n", [Fun, Result]),
            {error, {assert_raises, Error, Result}}
    end.
assert_raises(Fun, Args, Error) ->
    Result = (catch Fun(Args)),
    case Result of
        Error ->
            report(ok, "[pass]  .... ~p ( ~p ) raises ~p~n", [Fun, Args, Error]),
            ok;
        _ ->
            report(error, "[ERROR] .... ~p ( ~p ) raises ~p~n", [Fun, Args, Result]),
            {error, {assert_raises, Error, Result}}
    end.
            
%% Fail test
fail() ->
    report(error, "[ERROR] .... fail test~n", []),
    {error, {fail}}.


%%--------------------
%%
%% Internal functions
%%
%%--------------------
%% report test result
report(ok, Msg, Args) ->
    case lists:member(?MODULE, ets:all()) andalso
        ets:lookup_element(?MODULE, verbose, 2) of
        true ->
            io:format(Msg, Args);
        _ ->
            void
    end;
report(error, Msg, Args) ->
    io:format(Msg, Args).

%% Test setup
setup(Mod, FunList) ->
    case lists:keysearch(setup_test, 1, FunList) of
	{value, _} ->
	    Mod:setup_test();
	_ ->
	    done
    end.

%% Test finalization
end_t(Mod, FunList) ->
    case lists:keysearch(end_test, 1, FunList) of
	{value, _} ->
	    Mod:end_test();
	_ ->
	    done
    end.

%%
%% run all tests in module
%%
run_tests(_, [], ResultList) ->
    ResultList;
run_tests(Module, [H|T], ResultList) ->
    FName = element(1, H),
    Next = case lists:sublist(atom_to_list(FName), 5) of
        "test_" ->
	    try
                io:format("~nRunning ~p:~p~n", [Module, FName]),
                Module:FName()
	    catch
		Err:Reason -> 
                    io:format("[ERROR] .... ~p:~p ---> ~p~n", [Module, FName, {error, {Err, Reason}}])
	    end;
	_ ->
            ok
    end,
    run_tests(Module, T, [Next|ResultList]).


%%---------------------------------
%%
%% TEST to test the this module
%%
%%---------------------------------
test_ok() ->
    assert_equal(3+5, 8).
test_fail() ->
    assert_equal(3+5, 7).
test_not_equal() ->
    assert_not_equal(3+5, 7).
test_equal_list() ->
    assert_equal([3+5, 8, 7+1, 6+2, 4+4, 5+3]).
test_multiple() ->
    T1 = fun test_ok/0,
    T2 = fun test_fail/0,
    T3 = fun test_not_equal/0,
    [X() || X <- [T1, T2, T3]].
test_raises() ->
    Test = fun() -> throw({error}) end,
    Test2 = fun(_Args) -> throw({error}) end,
    [assert_raises(Test, {error}), assert_raises(Test2, {foo}, {error})].
